package org.openea.eap.module.obpm.controller.admin.proxy;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.openea.eap.framework.security.core.LoginUser;
import org.openea.eap.framework.security.core.util.SecurityFrameworkUtils;
import org.openea.eap.module.obpm.service.obpm.ObmpClientService;
import org.openea.eap.module.obpm.service.obpm.ObpmUtil;
import org.springframework.core.annotation.Order;
import org.springframework.util.StreamUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URL;
import java.util.Map;

/**
 * OpenBPM Proxy
 * 备用方案，生产环境需要采用gateway
 *
 * 仅仅用于开发调试
 */
@Tag(name = "OpenBPM Proxy")
@RestController
@Validated
@Slf4j
public class ObpmProxyController {

    @Resource
    private ObmpClientService obmpClientService;

    // 前端
    // 后端文件及图片
    @SneakyThrows
    @RequestMapping(method = RequestMethod.GET,
            value = {"/obpm-admin/**", "/obpm-web1/**"
                    , "/obpm-api/static/**"
                    , "**/*.jpg", "**/bpm/instance/flowImage"})
    @Order(110)
    public void proxyWeb(HttpServletRequest request, HttpServletResponse response, @RequestHeader Map<String, String> headers) {

        String url = checkRequestUrl(request);
        String obpmUrl = obmpClientService.getProxyUrl(url);
        checkHeaderHost(headers, obpmUrl);
        checkHeaderAuth(headers);
        try {

            // 使用 HttpUtil 发送 GET 请求
            HttpResponse webRes = HttpUtil.createGet(obpmUrl)
                    .addHeaders(headers) // 添加请求头
                    .execute();
            response.setStatus(webRes.getStatus());
            webRes.headers().forEach((key, value) -> value.forEach(it -> {
                response.setHeader(key, it);
            }));
            // 设置缓存头
            if (url.endsWith(".jpg")) {
                response.setHeader("Cache-Control", "public, max-age=31536000"); // 1年缓存
            }

            StreamUtils.copy(webRes.bodyStream(), response.getOutputStream());

        } catch (Exception e) {
            // 处理异常并记录日志
            log.error("Error proxying request to URL: {}", obpmUrl, e);
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Error proxying request");
        }
    }

    @SneakyThrows
    @RequestMapping(method = RequestMethod.GET,
            value = {"/obpm-web1/etech/**"})
    @Order(105)
    public void proxyWebCust(HttpServletRequest request, HttpServletResponse response, @RequestHeader Map<String, String> headers) {

        String url = checkRequestUrl(request);
        String obpmUrl = obmpClientService.getProxyUrl(url);
        checkHeaderHost(headers, obpmUrl);
        checkHeaderAuth(headers);
        try {

            // 使用 HttpUtil 发送 GET 请求
            HttpResponse webRes = HttpUtil.createGet(obpmUrl)
                    .addHeaders(headers) // 添加请求头
                    .execute();
            response.setStatus(webRes.getStatus());
            webRes.headers().forEach((key, value) -> value.forEach(it -> {
                response.setHeader(key, it);
            }));

            // cust 定制代码处理
            if (url.endsWith(".html") || url.indexOf(".html?") > 0) {
                String body = webRes.body();
                // todo filterHtml
                log.debug("page={}, html=\r\n{}", url, body);
                response.getWriter().write(body);
            } else {
                StreamUtils.copy(webRes.bodyStream(), response.getOutputStream());
            }
        } catch (Exception e) {
            // 处理异常并记录日志
            log.error("Error proxying request to URL: {}", obpmUrl, e);
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Error proxying request");
        }
    }

    //    "/bpm/instance/**", "/bpm/task/**", "/bpm/form/**",
//    "/form/formDefData/**", "/form/formCustDialog/**",
//    "/form/formCustSql/**",
//    "/sys/tools/**","/sys/dataDict/**"
    @SneakyThrows
    @RequestMapping(method = RequestMethod.GET,
            value = {"/obpm-server/**", "/obpm-api/**", "/obpm/**"})
    @Order(200)
    public JSONObject proxyGet(HttpServletRequest request, @RequestHeader Map<String, String> headers) {
        String url = checkRequestUrl(request);
        try {
            String obpmUrl = obmpClientService.getProxyUrl(url);
            checkHeaderHost(headers, obpmUrl);
            checkHeaderAuth(headers);
            // 使用 HttpUtil 发送 GET 请求
            HttpResponse response = HttpUtil.createGet(obpmUrl)
                    .addHeaders(headers) // 添加请求头
                    .execute();
            return convertJsonResult(response.body());
        } catch (Exception e) {
            log.error("url={}, msg={}", url, e.getMessage(), e);
            JSONObject result = new JSONObject();
            result.put("msg", e.getMessage());
            return result;
        }
    }

    @SneakyThrows
    @RequestMapping(method = RequestMethod.POST,
            value = {"/obpm-server/**", "/obpm-api/**", "/obpm/**"})
    @Order(300)
    public JSONObject proxyPost(HttpServletRequest request, @RequestBody(required = false) String body, @RequestHeader Map<String, String> headers) {
        String url = checkRequestUrl(request);
        try {
            String obpmUrl = obmpClientService.getProxyUrl(url);
            checkHeaderHost(headers, obpmUrl);
            checkHeaderAuth(headers);
            // 使用 HttpUtil 发送 POST 请求
            HttpRequest request2 = HttpUtil.createPost(obpmUrl)
                    .addHeaders(headers); // 添加请求头
            if (ObjectUtils.isNotEmpty(body) && !"{}".equals(body)) {
                request2 = request2.body(body);
            } else {
                // 兼容，总是加上json数据
                request2 = request2.body("{\"noKey\":\"\"}");
            }
            HttpResponse response = request2.execute();
            return convertJsonResult(response.body());
        } catch (Exception e) {
            log.error("url={}, msg={}", url, e.getMessage(), e);
            JSONObject result = new JSONObject();
            result.put("msg", e.getMessage());
            return result;
        }
    }

    private String checkRequestUrl(HttpServletRequest request) {
        String url = request.getRequestURI();
        if (ObjectUtils.isNotEmpty(request.getQueryString())) {
            url += "?" + request.getQueryString();
        }
        return url;
    }

    @SneakyThrows
    private void checkHeaderHost(Map<String, String> headers, String url) {
        String strHost = null;
        URL url2 = new URL(url);

        strHost = url2.getHost();
        int port = url2.getPort();
        if (port != -1 && port != 80 && port != 443) {
            strHost += ":" + port;
        }
        //Host/Origin/Referer
        headers.put("host", strHost);

        // dev environment use localhost
        if (headers.containsKey("origin")
                && ((String) headers.get("origin")).indexOf("localhost") > 0) {
            headers.put("origin", url);
            headers.put("referer", url);
        }
    }

    private void checkHeaderAuth(Map<String, String> headers) {
        LoginUser user = SecurityFrameworkUtils.getLoginUser();
        if (user != null && ObjectUtils.isNotEmpty(user.getUserKey())) {
            //header: eap-user, eap-sign
            String userKey = user.getUserKey();
            headers.put("eap-user", userKey);
            headers.put("eap-sign", ObpmUtil.eapSign(userKey));
        } else {
            // mark only
            headers.put("eap-proxy", "true");
        }
    }

    private JSONObject convertJsonResult(String responseBody) {
        JSONObject jsonResult = JSONUtil.parseObj(responseBody);
        // code 转为数字
        if (jsonResult.containsKey("code")) {
            int code = jsonResult.getInt("code");
            jsonResult.set("code", code);
        }
        return jsonResult;
    }
}
